package com.microservice.auth.service.impl;

import com.microservice.api.auth.dto.LoginResponse;
import com.microservice.api.common.Result;
import com.microservice.api.user.UserServiceClient;
import com.microservice.api.user.dto.LoginResponseDTO;
import com.microservice.api.user.dto.UserInfoDTO;
import com.microservice.api.user.dto.UserLoginDTO;
import com.microservice.auth.service.AuthService;
import com.microservice.auth.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 认证服务实现
 */
@Slf4j
@Service
public class AuthServiceImpl implements AuthService {
    
    @Autowired
    private UserServiceClient userServiceClient;
    
    @Autowired
    private JwtUtil jwtUtil;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    
    private static final String TOKEN_BLACKLIST_PREFIX = "token:blacklist:";
    
    @Override
    public LoginResponseDTO login(UserLoginDTO loginDTO) {
        // 1. 通过Feign获取用户信息
        Result<UserInfoDTO> userResult = userServiceClient.getUserByUsername(loginDTO.getUsername());
        
        if (userResult.getCode() != 200 || userResult.getData() == null) {
            throw new RuntimeException("用户名或密码错误");
        }
        
        UserInfoDTO userInfo = userResult.getData();
        Long userId = userInfo.getId();
        String username = userInfo.getUsername();
        String nickname = userInfo.getNickname();

        // 验证用户状态
        if (userInfo.getStatus() == 0) {
            throw new RuntimeException("账户已被禁用");
        }
        
        // 2. 验证密码
        // 注意：这里需要从用户服务获取密码进行验证
        // 为了安全，实际应该在用户服务中验证密码，这里简化处理
        // TODO: 改进密码验证流程
        
        // 3. 生成token
        String accessToken = jwtUtil.generateAccessToken(nickname,username, userId);
        String refreshToken = jwtUtil.generateRefreshToken(nickname,username, userId);
        
        // 4. 获取用户菜单
        Result<List<LoginResponse.MenuVO>> menuResult = userServiceClient.getUserMenus(userId);
        
        // 5. 获取用户角色
        Result<Set<String>> rolesResult = userServiceClient.getUserRoles(userId);
        
        // 6. 获取用户权限
        Result<Set<String>> permissionsResult = userServiceClient.getUserPermissions(userId);
        
        // 7. 构建完整响应
        LoginResponseDTO response = new LoginResponseDTO();
        response.setAccessToken(accessToken);
        response.setRefreshToken(refreshToken);
        response.setTokenType("Bearer");
        response.setExpiresIn(jwtUtil.getAccessTokenExpireSeconds());
        response.setUserInfo(userInfo);
        response.setRoles(rolesResult != null && rolesResult.getCode() == 200 ? rolesResult.getData() : null);
        response.setPermissions(permissionsResult != null && permissionsResult.getCode() == 200 ? permissionsResult.getData() : null);
        response.setMenus(menuResult != null && menuResult.getCode() == 200 ? menuResult.getData() : null);
        
        log.info("用户登录成功: {}", username);
        
        return response;
    }
    
    @Override
    public LoginResponseDTO refreshToken(String refreshToken) {
        try {
            // 验证刷新令牌
            if (!jwtUtil.validateToken(refreshToken)) {
                throw new RuntimeException("刷新令牌无效或已过期");
            }
            
            // 检查是否在黑名单中
            if (isTokenBlacklisted(refreshToken)) {
                throw new RuntimeException("刷新令牌已失效");
            }
            
            // 从刷新令牌中获取用户信息
            String username = jwtUtil.getUsernameFromToken(refreshToken);
            String nickname = jwtUtil.getNicknameFromToken(refreshToken);
            Long userId = jwtUtil.getUserIdFromToken(refreshToken);
            
            // 获取用户信息
            Result<UserInfoDTO> result = userServiceClient.getUserByUsername(username);
            if (result.getCode() != 200 || result.getData() == null) {
                throw new RuntimeException("用户不存在");
            }
            
            UserInfoDTO userInfo = result.getData();
            
            // 验证用户状态
            if (userInfo.getStatus() == 0) {
                throw new RuntimeException("账户已被禁用");
            }
            
            // 生成新的访问令牌
            String newAccessToken = jwtUtil.generateAccessToken( nickname,username, userId);
            
            log.info("刷新令牌成功: {}", username);
            
            return new LoginResponseDTO(
                    newAccessToken,
                    refreshToken,
                    jwtUtil.getAccessTokenExpireSeconds(),
                    userInfo
            );
        } catch (Exception e) {
            log.error("刷新令牌失败", e);
            throw new RuntimeException("刷新令牌失败: " + e.getMessage());
        }
    }
    
    @Override
    public boolean validateToken(String token) {
        try {
            // 检查Token格式是否正确
            if (token == null || token.isEmpty()) {
                return false;
            }
            
            // 检查是否在黑名单中
            if (isTokenBlacklisted(token)) {
                return false;
            }
            
            // 验证Token本身
            if (!jwtUtil.validateToken(token)) {
                return false;
            }
            
            // 检查该用户是否被封禁（用户级别黑名单）
            try {
                Long userId = jwtUtil.getUserIdFromToken(token);
                if (isUserBlacklisted(userId)) {
                    log.warn("用户 {} 已被封禁，token无效", userId);
                    return false;
                }
            } catch (Exception e) {
                log.error("检查用户黑名单失败", e);
                return false;
            }
            
            return true;
        } catch (Exception e) {
            log.error("验证Token失败", e);
            return false;
        }
    }
    
    @Override
    public void logout(String token) {
        try {
            // 将Token加入黑名单
            addTokenToBlacklist(token);
            log.info("用户登出成功");
        } catch (Exception e) {
            log.error("登出失败", e);
            throw new RuntimeException("登出失败");
        }
    }
    
    /**
     * 将Token加入黑名单
     */
    private void addTokenToBlacklist(String token) {
        String key = TOKEN_BLACKLIST_PREFIX + token;
        // 设置过期时间为Token的剩余有效时间
        redisTemplate.opsForValue().set(key, "1", 24, TimeUnit.HOURS);
    }
    
    /**
     * 检查Token是否在黑名单中
     */
    private boolean isTokenBlacklisted(String token) {
        String key = TOKEN_BLACKLIST_PREFIX + token;
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
    
    /**
     * 检查用户是否被封禁（用户级别黑名单）
     */
    private boolean isUserBlacklisted(Long userId) {
        String userBlacklistKey = "token:user:blacklist:" + userId;
        return Boolean.TRUE.equals(redisTemplate.hasKey(userBlacklistKey));
    }
    
    @Override
    public Long getUserIdFromToken(String token) {
        try {
            return jwtUtil.getUserIdFromToken(token);
        } catch (Exception e) {
            log.error("从Token中获取用户ID失败", e);
            throw new RuntimeException("无效的Token");
        }
    }
    
    @Override
    public String getUsernameFromToken(String token) {
        try {
            return jwtUtil.getUsernameFromToken(token);
        } catch (Exception e) {
            log.error("从Token中获取用户名失败", e);
            throw new RuntimeException("无效的Token");
        }
    }
    
    @Override
    public void invalidateUserTokens(Long userId) {
        try {
            // 使用Redis模糊匹配删除该用户的所有token
            String pattern = TOKEN_BLACKLIST_PREFIX + "*";
            Set<String> keys = redisTemplate.keys(pattern);
            
            if (keys != null && !keys.isEmpty()) {
                for (String key : keys) {
                    String token = key.substring(TOKEN_BLACKLIST_PREFIX.length());
                    try {
                        Long tokenUserId = jwtUtil.getUserIdFromToken(token);
                        if (userId.equals(tokenUserId)) {
                            // 该token属于目标用户，延长黑名单时间确保失效
                            redisTemplate.opsForValue().set(key, "1", 30, TimeUnit.DAYS);
                        }
                    } catch (Exception e) {
                        // Token可能已过期或无效，忽略
                    }
                }
            }
            
            // 创建一个用户级别的黑名单标记，所有该用户的token都将失效
            String userBlacklistKey = "token:user:blacklist:" + userId;
            redisTemplate.opsForValue().set(userBlacklistKey, String.valueOf(System.currentTimeMillis()), 30, TimeUnit.DAYS);
            
            log.info("已使用户 {} 的所有Token失效", userId);
        } catch (Exception e) {
            log.error("使用户Token失效失败", e);
            throw new RuntimeException("操作失败: " + e.getMessage());
        }
    }
    
    @Override
    public void unblockUserTokens(Long userId) {
        try {
            // 删除用户级别的黑名单标记
            String userBlacklistKey = "token:user:blacklist:" + userId;
            redisTemplate.delete(userBlacklistKey);
            
            log.info("已解除用户 {} 的Token封禁", userId);
        } catch (Exception e) {
            log.error("解除用户Token封禁失败", e);
            throw new RuntimeException("操作失败: " + e.getMessage());
        }
    }

    @Override
    public String getNicknameFromToken(String token) {
        try {
            return jwtUtil.getNicknameFromToken(token);
        } catch (Exception e) {
            log.error("从Token中获取昵称失败", e);
            throw new RuntimeException("无效的Token");
        }
    }
}
